home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / ui_servers2.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  41.0 KB  |  1,510 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. /*
  4. =======================================================================
  5.  
  6. MULTIPLAYER MENU (SERVER BROWSER)
  7.  
  8. =======================================================================
  9. */
  10.  
  11.  
  12. #include "ui_local.h"
  13.  
  14.  
  15. #define MAX_GLOBALSERVERS        128
  16. #define MAX_PINGREQUESTS        16
  17. #define MAX_ADDRESSLENGTH        64
  18. #define MAX_HOSTNAMELENGTH        22
  19. #define MAX_MAPNAMELENGTH        16
  20. #define MAX_LISTBOXITEMS        128
  21. #define MAX_LOCALSERVERS        128
  22. #define MAX_STATUSLENGTH        64
  23. #define MAX_LISTBOXWIDTH        53
  24.  
  25. #define ART_BACK0                "menu/art/back_0"
  26. #define ART_BACK1                "menu/art/back_1"
  27. #define ART_CREATE0                "menu/art/create_0"
  28. #define ART_CREATE1                "menu/art/create_1"
  29. #define ART_SPECIFY0            "menu/art/specify_0"
  30. #define ART_SPECIFY1            "menu/art/specify_1"
  31. #define ART_REFRESH0            "menu/art/refresh_0"
  32. #define ART_REFRESH1            "menu/art/refresh_1"
  33. #define ART_CONNECT0            "menu/art/fight_0"
  34. #define ART_CONNECT1            "menu/art/fight_1"
  35. #define ART_ARROWS0                "menu/art/arrows_vert_0"
  36. #define ART_ARROWS_UP            "menu/art/arrows_vert_top"
  37. #define ART_ARROWS_DOWN            "menu/art/arrows_vert_bot"
  38. #define ART_UNKNOWNMAP            "menu/art/unknownmap"
  39. #define ART_REMOVE0                "menu/art/delete_0"
  40. #define ART_REMOVE1                "menu/art/delete_1"
  41.  
  42. #define ID_MASTER            10
  43. #define ID_GAMETYPE            11
  44. #define ID_SORTKEY            12
  45. #define ID_SHOW_FULL        13
  46. #define ID_SHOW_EMPTY        14
  47. #define ID_LIST                15
  48. #define ID_SCROLL_UP        16
  49. #define ID_SCROLL_DOWN        17
  50. #define ID_BACK                18
  51. #define ID_REFRESH            19
  52. #define ID_SPECIFY            20
  53. #define ID_CREATE            21
  54. #define ID_CONNECT            22
  55. #define ID_REMOVE            23
  56.  
  57. #define AS_LOCAL            0
  58. #define AS_MPLAYER            1
  59. #define AS_GLOBAL            2
  60. #define AS_FAVORITES        3
  61.  
  62. #define SORT_HOST            0
  63. #define SORT_MAP            1
  64. #define SORT_CLIENTS        2
  65. #define SORT_GAME            3
  66. #define SORT_PING            4
  67.  
  68. #define GAMES_ALL            0
  69. #define GAMES_FFA            1
  70. #define GAMES_TEAMPLAY        2
  71. #define GAMES_TOURNEY        3
  72. #define GAMES_CTF            4
  73.  
  74. static const char *master_items[] = {
  75.     "Local",
  76.     "Mplayer",
  77.     "Internet",
  78.     "Favorites",
  79.     0
  80. };
  81.  
  82. static const char *servertype_items[] = {
  83.     "All",
  84.     "Free For All",
  85.     "Team Deathmatch",
  86.     "Tournament",
  87.     "Capture the Flag",
  88.     0
  89. };
  90.  
  91. static const char *sortkey_items[] = {
  92.     "Server Name",
  93.     "Map Name",
  94.     "Open Player Spots",
  95.     "Game Type",
  96.     "Ping Time",
  97.     0
  98. };
  99.  
  100. static char* gamenames[] = {
  101.     "DM ",    // deathmatch
  102.     "1V1",    // tournament
  103.     "SP ",    // single player
  104.     "TDM",    // team deathmatch
  105.     "CTF",    // capture the flag
  106.     "???",    // unknown
  107. };
  108.  
  109. static char* netnames[] = {
  110.     "???",
  111.     "UDP",
  112.     "IPX",
  113.     NULL
  114. };
  115.  
  116. static char quake3worldMessage[] = "Visit www.quake3world.com - News, Community, Events, Files";
  117.  
  118.  
  119. typedef struct {
  120.     char    adrstr[MAX_ADDRESSLENGTH];
  121.     int        start;
  122. } pinglist_t;
  123.  
  124. typedef struct servernode_s {
  125.     char    adrstr[MAX_ADDRESSLENGTH];
  126.     char    hostname[MAX_HOSTNAMELENGTH];
  127.     char    mapname[MAX_MAPNAMELENGTH];
  128.     int        numclients;
  129.     int        maxclients;
  130.     int        pingtime;
  131.     int        gametype;
  132.     int        nettype;
  133. } servernode_t; 
  134.  
  135. typedef struct {
  136.     char            buff[MAX_LISTBOXWIDTH];
  137.     servernode_t*    servernode;
  138. } table_t;
  139.  
  140. typedef struct {
  141.     menuframework_s        menu;
  142.  
  143.     menutext_s            banner;
  144.  
  145.     menulist_s            master;
  146.     menulist_s            gametype;
  147.     menulist_s            sortkey;
  148.     menuradiobutton_s    showfull;
  149.     menuradiobutton_s    showempty;
  150.  
  151.     menulist_s            list;
  152.     menubitmap_s        mappic;
  153.     menubitmap_s        arrows;
  154.     menubitmap_s        up;
  155.     menubitmap_s        down;
  156.     menutext_s            status;
  157.     menutext_s            statusbar;
  158.  
  159.     menubitmap_s        remove;
  160.     menubitmap_s        back;
  161.     menubitmap_s        refresh;
  162.     menubitmap_s        specify;
  163.     menubitmap_s        create;
  164.     menubitmap_s        go;
  165.  
  166.     pinglist_t            pinglist[MAX_PINGREQUESTS];
  167.     table_t                table[MAX_LISTBOXITEMS];
  168.     char*                items[MAX_LISTBOXITEMS];
  169.     int                    numqueriedservers;
  170.     int                    *numservers;
  171.     servernode_t        *serverlist;    
  172.     int                    currentping;
  173.     qboolean            refreshservers;
  174.     int                    nextpingtime;
  175.     int                    maxservers;
  176.     int                    refreshtime;
  177.     char                favoriteaddresses[MAX_FAVORITESERVERS][MAX_ADDRESSLENGTH];
  178.     int                    numfavoriteaddresses;
  179. } arenaservers_t;
  180.  
  181. static arenaservers_t    g_arenaservers;
  182.  
  183.  
  184. static servernode_t        g_globalserverlist[MAX_GLOBALSERVERS];
  185. static int                g_numglobalservers;
  186. static servernode_t        g_localserverlist[MAX_LOCALSERVERS];
  187. static int                g_numlocalservers;
  188. static servernode_t        g_favoriteserverlist[MAX_FAVORITESERVERS];
  189. static int                g_numfavoriteservers;
  190. static servernode_t        g_mplayerserverlist[MAX_GLOBALSERVERS];
  191. static int                g_nummplayerservers;
  192. static int                g_servertype;
  193. static int                g_gametype;
  194. static int                g_sortkey;
  195. static int                g_emptyservers;
  196. static int                g_fullservers;
  197.  
  198.  
  199. /*
  200. =================
  201. ArenaServers_MaxPing
  202. =================
  203. */
  204. static int ArenaServers_MaxPing( void ) {
  205.     int        maxPing;
  206.  
  207.     maxPing = (int)trap_Cvar_VariableValue( "cl_maxPing" );
  208.     if( maxPing < 100 ) {
  209.         maxPing = 100;
  210.     }
  211.     return maxPing;
  212. }
  213.  
  214.  
  215. /*
  216. =================
  217. ArenaServers_Compare
  218. =================
  219. */
  220. static int QDECL ArenaServers_Compare( const void *arg1, const void *arg2 ) {
  221.     float            f1;
  222.     float            f2;
  223.     servernode_t*    t1;
  224.     servernode_t*    t2;
  225.  
  226.     t1 = (servernode_t *)arg1;
  227.     t2 = (servernode_t *)arg2;
  228.  
  229.     switch( g_sortkey ) {
  230.     case SORT_HOST:
  231.         return Q_stricmp( t1->hostname, t2->hostname );
  232.  
  233.     case SORT_MAP:
  234.         return Q_stricmp( t1->mapname, t2->mapname );
  235.  
  236.     case SORT_CLIENTS:
  237.         f1 = t1->maxclients - t1->numclients;
  238.         if( f1 < 0 ) {
  239.             f1 = 0;
  240.         }
  241.  
  242.         f2 = t2->maxclients - t2->numclients;
  243.         if( f2 < 0 ) {
  244.             f2 = 0;
  245.         }
  246.  
  247.         if( f1 < f2 ) {
  248.             return 1;
  249.         }
  250.         if( f1 == f2 ) {
  251.             return 0;
  252.         }
  253.         return -1;
  254.  
  255.     case SORT_GAME:
  256.         if( t1->gametype < t2->gametype ) {
  257.             return -1;
  258.         }
  259.         if( t1->gametype == t2->gametype ) {
  260.             return 0;
  261.         }
  262.         return 1;
  263.  
  264.     case SORT_PING:
  265.         if( t1->pingtime < t2->pingtime ) {
  266.             return -1;
  267.         }
  268.         if( t1->pingtime > t2->pingtime ) {
  269.             return 1;
  270.         }
  271.         return Q_stricmp( t1->hostname, t2->hostname );
  272.     }
  273.  
  274.     return 0;
  275. }
  276.  
  277.  
  278. /*
  279. =================
  280. ArenaServers_Go
  281. =================
  282. */
  283. static void ArenaServers_Go( void ) {
  284.     servernode_t*    servernode;
  285.  
  286.     servernode = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
  287.     if( servernode ) {
  288.         trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", servernode->adrstr ) );
  289.     }
  290. }
  291.  
  292.  
  293. /*
  294. =================
  295. ArenaServers_UpdatePicture
  296. =================
  297. */
  298. static void ArenaServers_UpdatePicture( void ) {
  299.     static char        picname[64];
  300.     servernode_t*    servernodeptr;
  301.  
  302.     if( !g_arenaservers.list.numitems ) {
  303.         g_arenaservers.mappic.generic.name = NULL;
  304.     }
  305.     else {
  306.         servernodeptr = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
  307.         Com_sprintf( picname, sizeof(picname), "levelshots/%s.tga", servernodeptr->mapname );
  308.         g_arenaservers.mappic.generic.name = picname;
  309.     }
  310.  
  311.     // force shader update during draw
  312.     g_arenaservers.mappic.shader = 0;
  313. }
  314.  
  315.  
  316. /*
  317. =================
  318. ArenaServers_UpdateMenu
  319. =================
  320. */
  321. static void ArenaServers_UpdateMenu( void ) {
  322.     int                i;
  323.     int                j;
  324.     int                count;
  325.     char*            buff;
  326.     servernode_t*    servernodeptr;
  327.     table_t*        tableptr;
  328.  
  329.     if( g_arenaservers.numqueriedservers > 0 ) {
  330.         // servers found
  331.         if( g_arenaservers.refreshservers && ( g_arenaservers.currentping <= g_arenaservers.numqueriedservers ) ) {
  332.             // show progress
  333.             Com_sprintf( g_arenaservers.status.string, MAX_STATUSLENGTH, "%d of %d Arena Servers.", g_arenaservers.currentping, g_arenaservers.numqueriedservers);
  334.             g_arenaservers.statusbar.string  = "Press SPACE to stop";
  335.             qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
  336.         }
  337.         else {
  338.             // all servers pinged - enable controls
  339.             g_arenaservers.master.generic.flags        &= ~QMF_GRAYED;
  340.             g_arenaservers.gametype.generic.flags    &= ~QMF_GRAYED;
  341.             g_arenaservers.sortkey.generic.flags    &= ~QMF_GRAYED;
  342.             g_arenaservers.showempty.generic.flags    &= ~QMF_GRAYED;
  343.             g_arenaservers.showfull.generic.flags    &= ~QMF_GRAYED;
  344.             g_arenaservers.list.generic.flags        &= ~QMF_GRAYED;
  345.             g_arenaservers.refresh.generic.flags    &= ~QMF_GRAYED;
  346.             g_arenaservers.go.generic.flags            &= ~QMF_GRAYED;
  347.  
  348.             // update status bar
  349.             if( g_servertype == AS_GLOBAL || g_servertype == AS_MPLAYER ) {
  350.                 g_arenaservers.statusbar.string = quake3worldMessage;
  351.             }
  352.             else {
  353.                 g_arenaservers.statusbar.string = "";
  354.             }
  355.         }
  356.     }
  357.     else {
  358.         // no servers found
  359.         if( g_arenaservers.refreshservers ) {
  360.             strcpy( g_arenaservers.status.string,"Scanning For Servers." );
  361.             g_arenaservers.statusbar.string = "Press SPACE to stop";
  362.  
  363.             // disable controls during refresh
  364.             g_arenaservers.master.generic.flags        |= QMF_GRAYED;
  365.             g_arenaservers.gametype.generic.flags    |= QMF_GRAYED;
  366.             g_arenaservers.sortkey.generic.flags    |= QMF_GRAYED;
  367.             g_arenaservers.showempty.generic.flags    |= QMF_GRAYED;
  368.             g_arenaservers.showfull.generic.flags    |= QMF_GRAYED;
  369.             g_arenaservers.list.generic.flags        |= QMF_GRAYED;
  370.             g_arenaservers.refresh.generic.flags    |= QMF_GRAYED;
  371.             g_arenaservers.go.generic.flags            |= QMF_GRAYED;
  372.         }
  373.         else {
  374.             if( g_arenaservers.numqueriedservers < 0 ) {
  375.                 strcpy(g_arenaservers.status.string,"No Response From Master Server." );
  376.             }
  377.             else {
  378.                 strcpy(g_arenaservers.status.string,"No Servers Found." );
  379.             }
  380.  
  381.             // update status bar
  382.             if( g_servertype == AS_GLOBAL || g_servertype == AS_MPLAYER ) {
  383.                 g_arenaservers.statusbar.string = quake3worldMessage;
  384.             }
  385.             else {
  386.                 g_arenaservers.statusbar.string = "";
  387.             }
  388.  
  389.             // end of refresh - set control state
  390.             g_arenaservers.master.generic.flags        &= ~QMF_GRAYED;
  391.             g_arenaservers.gametype.generic.flags    &= ~QMF_GRAYED;
  392.             g_arenaservers.sortkey.generic.flags    &= ~QMF_GRAYED;
  393.             g_arenaservers.showempty.generic.flags    &= ~QMF_GRAYED;
  394.             g_arenaservers.showfull.generic.flags    &= ~QMF_GRAYED;
  395.             g_arenaservers.list.generic.flags        |= QMF_GRAYED;
  396.             g_arenaservers.refresh.generic.flags    &= ~QMF_GRAYED;
  397.             g_arenaservers.go.generic.flags            |= QMF_GRAYED;
  398.         }
  399.  
  400.         // zero out list box
  401.         g_arenaservers.list.numitems = 0;
  402.         g_arenaservers.list.curvalue = 0;
  403.         g_arenaservers.list.top      = 0;
  404.  
  405.         // update picture
  406.         ArenaServers_UpdatePicture();
  407.         return;
  408.     }
  409.  
  410.     // build list box strings - apply culling filters
  411.     servernodeptr = g_arenaservers.serverlist;
  412.     count         = *g_arenaservers.numservers;
  413.     for( i = 0, j = 0; i < count; i++, servernodeptr++ ) {
  414.         tableptr = &g_arenaservers.table[j];
  415.         tableptr->servernode = servernodeptr;
  416.         buff = tableptr->buff;
  417.  
  418.         // can only cull valid results
  419.         if( !g_emptyservers && !servernodeptr->numclients ) {
  420.             continue;
  421.         }
  422.  
  423.         if( !g_fullservers && ( servernodeptr->numclients == servernodeptr->maxclients ) ) {
  424.             continue;
  425.         }
  426.  
  427.         switch( g_gametype ) {
  428.         case GAMES_ALL:
  429.             break;
  430.  
  431.         case GAMES_FFA:
  432.             if( servernodeptr->gametype != GT_FFA ) {
  433.                 continue;
  434.             }
  435.             break;
  436.  
  437.         case GAMES_TEAMPLAY:
  438.             if( servernodeptr->gametype != GT_TEAM ) {
  439.                 continue;
  440.             }
  441.             break;
  442.  
  443.         case GAMES_TOURNEY:
  444.             if( servernodeptr->gametype != GT_TOURNAMENT ) {
  445.                 continue;
  446.             }
  447.             break;
  448.  
  449.         case GAMES_CTF:
  450.             if( servernodeptr->gametype != GT_CTF ) {
  451.                 continue;
  452.             }
  453.             break;
  454.         }
  455.  
  456.         Com_sprintf( buff, MAX_LISTBOXWIDTH, "%-20.20s %-12.12s %2d/%2d %4s %3s %3d", 
  457.             servernodeptr->hostname, servernodeptr->mapname, servernodeptr->numclients,
  458.              servernodeptr->maxclients, gamenames[servernodeptr->gametype],
  459.             netnames[servernodeptr->nettype], servernodeptr->pingtime );
  460.         j++;
  461.     }
  462.  
  463. //    Com_sprintf( g_arenaservers.status.string, MAX_STATUSLENGTH, "%d of %d Arena Servers.", j, *g_arenaservers.numservers );
  464.  
  465.     g_arenaservers.list.numitems = j;
  466.     g_arenaservers.list.curvalue = 0;
  467.     g_arenaservers.list.top      = 0;
  468.  
  469.     // update picture
  470.     ArenaServers_UpdatePicture();
  471. }
  472.  
  473.  
  474. /*
  475. =================
  476. ArenaServers_Remove
  477. =================
  478. */
  479. static void ArenaServers_Remove( void )
  480. {
  481.     int                i;
  482.     servernode_t*    servernodeptr;
  483.     table_t*        tableptr;
  484.  
  485.     if (!g_arenaservers.list.numitems)
  486.         return;
  487.  
  488.     // remove selected item from display list
  489.     // items are in scattered order due to sort and cull
  490.     // perform delete on list box contents, resync all lists
  491.  
  492.     tableptr      = &g_arenaservers.table[g_arenaservers.list.curvalue];
  493.     servernodeptr = tableptr->servernode;
  494.  
  495.     // find address in master list
  496.     for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
  497.         if (!Q_stricmp(g_arenaservers.favoriteaddresses[i],servernodeptr->adrstr))
  498.                 break;
  499.  
  500.     // delete address from master list
  501.     if (i <= g_arenaservers.numfavoriteaddresses-1)
  502.     {
  503.         if (i < g_arenaservers.numfavoriteaddresses-1)
  504.         {
  505.             // shift items up
  506.             memcpy( &g_arenaservers.favoriteaddresses[i], &g_arenaservers.favoriteaddresses[i+1], (g_arenaservers.numfavoriteaddresses - i - 1)*sizeof(MAX_ADDRESSLENGTH));
  507.         }
  508.         g_arenaservers.numfavoriteaddresses--;
  509.     }    
  510.  
  511.     // find address in server list
  512.     for (i=0; i<g_numfavoriteservers; i++)
  513.         if (&g_favoriteserverlist[i] == servernodeptr)
  514.                 break;
  515.  
  516.     // delete address from server list
  517.     if (i <= g_numfavoriteservers-1)
  518.     {
  519.         if (i < g_numfavoriteservers-1)
  520.         {
  521.             // shift items up
  522.             memcpy( &g_favoriteserverlist[i], &g_favoriteserverlist[i+1], (g_numfavoriteservers - i - 1)*sizeof(servernode_t));
  523.         }
  524.         g_numfavoriteservers--;
  525.     }    
  526.  
  527.     g_arenaservers.numqueriedservers = g_arenaservers.numfavoriteaddresses;
  528.     g_arenaservers.currentping       = g_arenaservers.numfavoriteaddresses;
  529. }
  530.  
  531.  
  532. /*
  533. =================
  534. ArenaServers_Insert
  535. =================
  536. */
  537. static void ArenaServers_Insert( char* adrstr, char* info, int pingtime )
  538. {
  539.     servernode_t*    servernodeptr;
  540.     char*            s;
  541.     int                i;
  542.  
  543.     if ((pingtime >= ArenaServers_MaxPing()) && (g_servertype != AS_FAVORITES))
  544.     {
  545.         // slow global or local servers do not get entered
  546.         return;
  547.     }
  548.  
  549.     if (*g_arenaservers.numservers >= g_arenaservers.maxservers) {
  550.         // list full;
  551.         servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers)-1;
  552.     } else {
  553.         // next slot
  554.         servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers);
  555.         (*g_arenaservers.numservers)++;
  556.     }
  557.  
  558.     Q_strncpyz( servernodeptr->adrstr, adrstr, MAX_ADDRESSLENGTH );
  559.  
  560.     Q_strncpyz( servernodeptr->hostname, Info_ValueForKey( info, "hostname"), MAX_HOSTNAMELENGTH );
  561.     Q_CleanStr( servernodeptr->hostname );
  562.     Q_strupr( servernodeptr->hostname );
  563.  
  564.     Q_strncpyz( servernodeptr->mapname, Info_ValueForKey( info, "mapname"), MAX_MAPNAMELENGTH );
  565.     Q_CleanStr( servernodeptr->mapname );
  566.     Q_strupr( servernodeptr->mapname );
  567.  
  568.     servernodeptr->numclients = atoi( Info_ValueForKey( info, "clients") );
  569.     servernodeptr->maxclients = atoi( Info_ValueForKey( info, "sv_maxclients") );
  570.     servernodeptr->pingtime   = pingtime;
  571.  
  572.     s = Info_ValueForKey( info, "nettype" );
  573.     for (i=0; ;i++)
  574.     {
  575.         if (!netnames[i])
  576.         {
  577.             servernodeptr->nettype = 0;
  578.             break;
  579.         }
  580.         else if (!Q_stricmp( netnames[i], s ))
  581.         {
  582.             servernodeptr->nettype = i;
  583.             break;
  584.         }
  585.     }
  586.  
  587.     i = atoi( Info_ValueForKey( info, "gametype") );
  588.     if (i < 0 || i > 5)
  589.         i = 5;
  590.     servernodeptr->gametype = i;
  591.  
  592. }
  593.  
  594.  
  595. /*
  596. =================
  597. ArenaServers_InsertFavorites
  598.  
  599. Insert nonresponsive address book entries into display lists.
  600. =================
  601. */
  602. void ArenaServers_InsertFavorites( void )
  603. {
  604.     int        i;
  605.     int        j;
  606.     char    info[8];
  607.  
  608.     // resync existing results with new or deleted cvars
  609.     info[0] = '\0';
  610.     for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
  611.     {
  612.         // find favorite address in refresh list
  613.         for (j=0; j<g_numfavoriteservers; j++)
  614.             if (!Q_stricmp(g_arenaservers.favoriteaddresses[i],g_favoriteserverlist[j].adrstr))
  615.                 break;
  616.  
  617.         if ( j >= g_numfavoriteservers)
  618.         {
  619.             // not in list, add it
  620.             ArenaServers_Insert( g_arenaservers.favoriteaddresses[i], info, ArenaServers_MaxPing() );
  621.         }
  622.     }
  623. }
  624.  
  625.  
  626. /*
  627. =================
  628. ArenaServers_LoadFavorites
  629.  
  630. Load cvar address book entries into local lists.
  631. =================
  632. */
  633. void ArenaServers_LoadFavorites( void )
  634. {
  635.     int                i;
  636.     int                j;
  637.     int                numtempitems;
  638.     char            emptyinfo[MAX_INFO_STRING];
  639.     char            adrstr[MAX_ADDRESSLENGTH];
  640.     servernode_t    templist[MAX_FAVORITESERVERS];
  641.     qboolean        found;
  642.  
  643.     found        = qfalse;
  644.     emptyinfo[0] = '\0';
  645.  
  646.     // copy the old
  647.     memcpy( templist, g_favoriteserverlist, sizeof(servernode_t)*MAX_FAVORITESERVERS );
  648.     numtempitems = g_numfavoriteservers;
  649.  
  650.     // clear the current for sync
  651.     memset( g_favoriteserverlist, 0, sizeof(servernode_t)*MAX_FAVORITESERVERS );
  652.     g_numfavoriteservers = 0;
  653.  
  654.     // resync existing results with new or deleted cvars
  655.     for (i=0; i<MAX_FAVORITESERVERS; i++)
  656.     {
  657.         trap_Cvar_VariableStringBuffer( va("server%d",i+1), adrstr, MAX_ADDRESSLENGTH );
  658.         if (!adrstr[0])
  659.             continue;
  660.  
  661.         // quick sanity check to avoid slow domain name resolving
  662.         // first character must be numeric
  663.         if (adrstr[0] < '0' || adrstr[0] > '9')
  664.             continue;
  665.  
  666.         // favorite server addresses must be maintained outside refresh list
  667.         // this mimics local and global netadr's stored in client
  668.         // these can be fetched to fill ping list
  669.         strcpy( g_arenaservers.favoriteaddresses[g_numfavoriteservers], adrstr );
  670.  
  671.         // find this server in the old list
  672.         for (j=0; j<numtempitems; j++)
  673.             if (!Q_stricmp( templist[j].adrstr, adrstr ))
  674.                 break;
  675.  
  676.         if (j < numtempitems)
  677.         {
  678.             // found server - add exisiting results
  679.             memcpy( &g_favoriteserverlist[g_numfavoriteservers], &templist[j], sizeof(servernode_t) );
  680.             found = qtrue;
  681.         }
  682.         else
  683.         {
  684.             // add new server
  685.             Q_strncpyz( g_favoriteserverlist[g_numfavoriteservers].adrstr, adrstr, MAX_ADDRESSLENGTH );
  686.             g_favoriteserverlist[g_numfavoriteservers].pingtime = ArenaServers_MaxPing();
  687.         }
  688.  
  689.         g_numfavoriteservers++;
  690.     }
  691.  
  692.     g_arenaservers.numfavoriteaddresses = g_numfavoriteservers;
  693.  
  694.     if (!found)
  695.     {
  696.         // no results were found, reset server list
  697.         // list will be automatically refreshed when selected
  698.         g_numfavoriteservers = 0;
  699.     }
  700. }
  701.  
  702.  
  703. /*
  704. =================
  705. ArenaServers_StopRefresh
  706. =================
  707. */
  708. static void ArenaServers_StopRefresh( void )
  709. {
  710.     if (!g_arenaservers.refreshservers)
  711.         // not currently refreshing
  712.         return;
  713.  
  714.     g_arenaservers.refreshservers = qfalse;
  715.  
  716.     if (g_servertype == AS_FAVORITES)
  717.     {
  718.         // nonresponsive favorites must be shown
  719.         ArenaServers_InsertFavorites();
  720.     }
  721.  
  722.     // final tally
  723.     if (g_arenaservers.numqueriedservers >= 0)
  724.     {
  725.         g_arenaservers.currentping       = *g_arenaservers.numservers;
  726.         g_arenaservers.numqueriedservers = *g_arenaservers.numservers; 
  727.     }
  728.     
  729.     // sort
  730.     qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
  731.  
  732.     ArenaServers_UpdateMenu();
  733. }
  734.  
  735.  
  736. /*
  737. =================
  738. ArenaServers_DoRefresh
  739. =================
  740. */
  741. static void ArenaServers_DoRefresh( void )
  742. {
  743.     int        i;
  744.     int        j;
  745.     int        time;
  746.     int        maxPing;
  747.     char    adrstr[MAX_ADDRESSLENGTH];
  748.     char    info[MAX_INFO_STRING];
  749.  
  750.     if (uis.realtime < g_arenaservers.refreshtime)
  751.     {
  752.         // wait for timeout or results
  753.         if (g_servertype == AS_LOCAL)
  754.         {
  755.             if (!trap_LAN_GetLocalServerCount())
  756.             {
  757.                 // still waiting for response
  758.                 return;
  759.             }
  760.         }
  761.         else if (g_servertype == AS_GLOBAL)
  762.         {
  763.             if (trap_LAN_GetGlobalServerCount() < 0)
  764.             {
  765.                 // still waiting for master server response
  766.                 return;
  767.             }
  768.         }
  769.         else if (g_servertype == AS_MPLAYER)
  770.         {
  771.             if (trap_LAN_GetGlobalServerCount() < 0)
  772.             {
  773.                 // still waiting for master server response
  774.                 return;
  775.             }
  776.         }
  777.     }
  778.  
  779.     if (uis.realtime < g_arenaservers.nextpingtime)
  780.     {
  781.         // wait for time trigger
  782.         return;
  783.     }
  784.  
  785.     // trigger at 10Hz intervals
  786.     g_arenaservers.nextpingtime = uis.realtime + 50;
  787.  
  788.     // process ping results
  789.     maxPing = ArenaServers_MaxPing();
  790.     for (i=0; i<MAX_PINGREQUESTS; i++)
  791.     {
  792.         trap_LAN_GetPing( i, adrstr, MAX_ADDRESSLENGTH, &time );
  793.         if (!adrstr[0])
  794.         {
  795.             // ignore empty or pending pings
  796.             continue;
  797.         }
  798.  
  799.         // find ping result in our local list
  800.         for (j=0; j<MAX_PINGREQUESTS; j++)
  801.             if (!Q_stricmp( adrstr, g_arenaservers.pinglist[j].adrstr ))
  802.                 break;
  803.  
  804.         if (j < MAX_PINGREQUESTS)
  805.         {
  806.             // found it
  807.             if (!time)
  808.             {
  809.                 time = uis.realtime - g_arenaservers.pinglist[j].start;
  810.                 if (time < maxPing)
  811.                 {
  812.                     // still waiting
  813.                     continue;
  814.                 }
  815.             }
  816.  
  817.             if (time > maxPing)
  818.             {
  819.                 // stale it out
  820.                 info[0] = '\0';
  821.                 time    = maxPing;
  822.             }
  823.             else
  824.             {
  825.                 trap_LAN_GetPingInfo( i, info, MAX_INFO_STRING );
  826.             }
  827.  
  828.             // insert ping results
  829.             ArenaServers_Insert( adrstr, info, time );
  830.  
  831.             // clear this query from internal list
  832.             g_arenaservers.pinglist[j].adrstr[0] = '\0';
  833.            }
  834.  
  835.         // clear this query from external list
  836.         trap_LAN_ClearPing( i );
  837.     }
  838.  
  839.     // get results of servers query
  840.     // counts can increase as servers respond
  841.     switch (g_servertype)
  842.     {
  843.         case AS_LOCAL:
  844.             g_arenaservers.numqueriedservers = trap_LAN_GetLocalServerCount();
  845.             break;
  846.  
  847.         case AS_GLOBAL:
  848.             g_arenaservers.numqueriedservers = trap_LAN_GetGlobalServerCount();
  849.             break;
  850.  
  851.         case AS_FAVORITES:
  852.             g_arenaservers.numqueriedservers = g_arenaservers.numfavoriteaddresses;
  853.             break;
  854.  
  855.         case AS_MPLAYER:
  856.             g_arenaservers.numqueriedservers = trap_LAN_GetGlobalServerCount();
  857.             break;
  858.     }
  859.  
  860. //    if (g_arenaservers.numqueriedservers > g_arenaservers.maxservers)
  861. //        g_arenaservers.numqueriedservers = g_arenaservers.maxservers;
  862.  
  863.     // send ping requests in reasonable bursts
  864.     // iterate ping through all found servers
  865.     for (i=0; i<MAX_PINGREQUESTS && g_arenaservers.currentping < g_arenaservers.numqueriedservers; i++)
  866.     {
  867.         if (trap_LAN_GetPingQueueCount() >= MAX_PINGREQUESTS)
  868.         {
  869.             // ping queue is full
  870.             break;
  871.         }
  872.  
  873.         // find empty slot
  874.         for (j=0; j<MAX_PINGREQUESTS; j++)
  875.             if (!g_arenaservers.pinglist[j].adrstr[0])
  876.                 break;
  877.  
  878.         if (j >= MAX_PINGREQUESTS)
  879.             // no empty slots available yet - wait for timeout
  880.             break;
  881.  
  882.         // get an address to ping
  883.         switch (g_servertype)
  884.         {
  885.             case AS_LOCAL:    
  886.                 trap_LAN_GetLocalServerAddressString( g_arenaservers.currentping, adrstr, MAX_ADDRESSLENGTH );
  887.                 break;
  888.  
  889.             case AS_GLOBAL:
  890.                 trap_LAN_GetGlobalServerAddressString( g_arenaservers.currentping, adrstr, MAX_ADDRESSLENGTH );
  891.                 break;
  892.  
  893.             case AS_FAVORITES:
  894.                 strcpy( adrstr, g_arenaservers.favoriteaddresses[g_arenaservers.currentping] );         
  895.                 break;
  896.  
  897.             case AS_MPLAYER:
  898.                 trap_LAN_GetGlobalServerAddressString( g_arenaservers.currentping, adrstr, MAX_ADDRESSLENGTH );
  899.                 break;
  900.         }
  901.  
  902.         strcpy( g_arenaservers.pinglist[j].adrstr, adrstr );
  903.         g_arenaservers.pinglist[j].start = uis.realtime;
  904.  
  905.         trap_Cmd_ExecuteText( EXEC_NOW, va( "ping %s\n", adrstr )  );
  906.         
  907.         // advance to next server
  908.         g_arenaservers.currentping++;
  909.     }
  910.  
  911.     if (!trap_LAN_GetPingQueueCount())
  912.     {
  913.         // all pings completed
  914.         ArenaServers_StopRefresh();
  915.         return;
  916.     }
  917.  
  918.     // update the user interface with ping status
  919.     ArenaServers_UpdateMenu();
  920. }
  921.  
  922.  
  923. /*
  924. =================
  925. ArenaServers_StartRefresh
  926. =================
  927. */
  928. static void ArenaServers_StartRefresh( void )
  929. {
  930.     int        i;
  931.     char    myargs[32];
  932.  
  933.     memset( g_arenaservers.serverlist, 0, g_arenaservers.maxservers*sizeof(table_t) );
  934.  
  935.     for (i=0; i<MAX_PINGREQUESTS; i++)
  936.     {
  937.         g_arenaservers.pinglist[i].adrstr[0] = '\0';
  938.         trap_LAN_ClearPing( i );
  939.     }
  940.  
  941.     g_arenaservers.refreshservers    = qtrue;
  942.     g_arenaservers.currentping       = 0;
  943.     g_arenaservers.nextpingtime      = 0;
  944.     *g_arenaservers.numservers       = 0;
  945.     g_arenaservers.numqueriedservers = 0;
  946.  
  947.     // allow max 5 seconds for responses
  948.     g_arenaservers.refreshtime = uis.realtime + 5000;
  949.  
  950.     // place menu in zeroed state
  951.     ArenaServers_UpdateMenu();
  952.  
  953.     if( g_servertype == AS_LOCAL ) {
  954.         trap_Cmd_ExecuteText( EXEC_APPEND, "localservers\n" );
  955.         return;
  956.     }
  957.  
  958.     if( g_servertype == AS_GLOBAL || g_servertype == AS_MPLAYER ) {
  959.         if( g_servertype == AS_GLOBAL ) {
  960.             i = 0;
  961.         }
  962.         else {
  963.             i = 1;
  964.         }
  965.  
  966.         switch( g_arenaservers.gametype.curvalue ) {
  967.         default:
  968.         case GAMES_ALL:
  969.             myargs[0] = 0;
  970.             break;
  971.  
  972.         case GAMES_FFA:
  973.             strcpy( myargs, " ffa" );
  974.             break;
  975.  
  976.         case GAMES_TEAMPLAY:
  977.             strcpy( myargs, " team" );
  978.             break;
  979.  
  980.         case GAMES_TOURNEY:
  981.             strcpy( myargs, " tourney" );
  982.             break;
  983.  
  984.         case GAMES_CTF:
  985.             strcpy( myargs, " ctf" );
  986.             break;
  987.         }
  988.  
  989.  
  990.         if (g_emptyservers) {
  991.             strcat(myargs, " empty");
  992.         }
  993.  
  994.         if (g_fullservers) {
  995.             strcat(myargs, " full");
  996.         }
  997.  
  998.         trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %d%s\n", i, (int)trap_Cvar_VariableValue( "protocol" ), myargs ) );
  999.     }
  1000. }
  1001.  
  1002.  
  1003. /*
  1004. =================
  1005. ArenaServers_SaveChanges
  1006. =================
  1007. */
  1008. void ArenaServers_SaveChanges( void )
  1009. {
  1010.     int    i;
  1011.  
  1012.     for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
  1013.         trap_Cvar_Set( va("server%d",i+1), g_arenaservers.favoriteaddresses[i] );
  1014.  
  1015.     for (; i<MAX_FAVORITESERVERS; i++)
  1016.         trap_Cvar_Set( va("server%d",i+1), "" );
  1017. }
  1018.  
  1019.  
  1020. /*
  1021. =================
  1022. ArenaServers_Sort
  1023. =================
  1024. */
  1025. void ArenaServers_Sort( int type ) {
  1026.     if( g_sortkey == type ) {
  1027.         return;
  1028.     }
  1029.  
  1030.     g_sortkey = type;
  1031.     qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
  1032. }
  1033.  
  1034.  
  1035. /*
  1036. =================
  1037. ArenaServers_SetType
  1038. =================
  1039. */
  1040. void ArenaServers_SetType( int type )
  1041. {
  1042.     if (g_servertype == type)
  1043.         return;
  1044.  
  1045.     g_servertype = type;
  1046.  
  1047.     switch( type ) {
  1048.     default:
  1049.     case AS_LOCAL:
  1050.         g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
  1051.         g_arenaservers.serverlist = g_localserverlist;
  1052.         g_arenaservers.numservers = &g_numlocalservers;
  1053.         g_arenaservers.maxservers = MAX_LOCALSERVERS;
  1054.         break;
  1055.  
  1056.     case AS_GLOBAL:
  1057.         g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
  1058.         g_arenaservers.serverlist = g_globalserverlist;
  1059.         g_arenaservers.numservers = &g_numglobalservers;
  1060.         g_arenaservers.maxservers = MAX_GLOBALSERVERS;
  1061.         break;
  1062.  
  1063.     case AS_FAVORITES:
  1064.         g_arenaservers.remove.generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
  1065.         g_arenaservers.serverlist = g_favoriteserverlist;
  1066.         g_arenaservers.numservers = &g_numfavoriteservers;
  1067.         g_arenaservers.maxservers = MAX_FAVORITESERVERS;
  1068.         break;
  1069.  
  1070.     case AS_MPLAYER:
  1071.         g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
  1072.         g_arenaservers.serverlist = g_mplayerserverlist;
  1073.         g_arenaservers.numservers = &g_nummplayerservers;
  1074.         g_arenaservers.maxservers = MAX_GLOBALSERVERS;
  1075.         break;
  1076.     }
  1077.  
  1078.     if( !*g_arenaservers.numservers ) {
  1079.         ArenaServers_StartRefresh();
  1080.     }
  1081.     else {
  1082.         // avoid slow operation, use existing results
  1083.         g_arenaservers.currentping       = *g_arenaservers.numservers;
  1084.         g_arenaservers.numqueriedservers = *g_arenaservers.numservers; 
  1085.         ArenaServers_UpdateMenu();
  1086.     }
  1087.     strcpy(g_arenaservers.status.string,"hit refresh to update");
  1088. }
  1089.  
  1090.  
  1091. /*
  1092. =================
  1093. ArenaServers_Event
  1094. =================
  1095. */
  1096. static void ArenaServers_Event( void* ptr, int event ) {
  1097.     int        id;
  1098.  
  1099.     id = ((menucommon_s*)ptr)->id;
  1100.  
  1101.     if( event != QM_ACTIVATED && id != ID_LIST ) {
  1102.         return;
  1103.     }
  1104.  
  1105.     switch( id ) {
  1106.     case ID_MASTER:
  1107.         trap_Cvar_SetValue( "ui_browserMaster", g_arenaservers.master.curvalue );
  1108.         ArenaServers_SetType( g_arenaservers.master.curvalue );
  1109.         break;
  1110.  
  1111.     case ID_GAMETYPE:
  1112.         trap_Cvar_SetValue( "ui_browserGameType", g_arenaservers.gametype.curvalue );
  1113.         g_gametype = g_arenaservers.gametype.curvalue;
  1114.         ArenaServers_UpdateMenu();
  1115.         break;
  1116.  
  1117.     case ID_SORTKEY:
  1118.         trap_Cvar_SetValue( "ui_browserSortKey", g_arenaservers.sortkey.curvalue );
  1119.         ArenaServers_Sort( g_arenaservers.sortkey.curvalue );
  1120.         ArenaServers_UpdateMenu();
  1121.         break;
  1122.  
  1123.     case ID_SHOW_FULL:
  1124.         trap_Cvar_SetValue( "ui_browserShowFull", g_arenaservers.showfull.curvalue );
  1125.         g_fullservers = g_arenaservers.showfull.curvalue;
  1126.         ArenaServers_UpdateMenu();
  1127.         break;
  1128.  
  1129.     case ID_SHOW_EMPTY:
  1130.         trap_Cvar_SetValue( "ui_browserShowEmpty", g_arenaservers.showempty.curvalue );
  1131.         g_emptyservers = g_arenaservers.showempty.curvalue;
  1132.         ArenaServers_UpdateMenu();
  1133.         break;
  1134.  
  1135.     case ID_LIST:
  1136.         if( event == QM_GOTFOCUS ) {
  1137.             ArenaServers_UpdatePicture();
  1138.         }
  1139.         break;
  1140.  
  1141.     case ID_SCROLL_UP:
  1142.         ScrollList_Key( &g_arenaservers.list, K_UPARROW );
  1143.         break;
  1144.  
  1145.     case ID_SCROLL_DOWN:
  1146.         ScrollList_Key( &g_arenaservers.list, K_DOWNARROW );
  1147.         break;
  1148.  
  1149.     case ID_BACK:
  1150.         ArenaServers_StopRefresh();
  1151.         ArenaServers_SaveChanges();
  1152.         UI_PopMenu();
  1153.         break;
  1154.  
  1155.     case ID_REFRESH:
  1156.         ArenaServers_StartRefresh();
  1157.         break;
  1158.  
  1159.     case ID_SPECIFY:
  1160.         UI_SpecifyServerMenu();
  1161.         break;
  1162.  
  1163.     case ID_CREATE:
  1164.         UI_StartServerMenu( qtrue );
  1165.         break;
  1166.  
  1167.     case ID_CONNECT:
  1168.         ArenaServers_Go();
  1169.         break;
  1170.  
  1171.     case ID_REMOVE:
  1172.         ArenaServers_Remove();
  1173.         ArenaServers_UpdateMenu();
  1174.         break;
  1175.     }
  1176. }
  1177.  
  1178.  
  1179. /*
  1180. =================
  1181. ArenaServers_MenuDraw
  1182. =================
  1183. */
  1184. static void ArenaServers_MenuDraw( void )
  1185. {
  1186.     if (g_arenaservers.refreshservers)
  1187.         ArenaServers_DoRefresh();
  1188.  
  1189.     Menu_Draw( &g_arenaservers.menu );
  1190. }
  1191.  
  1192.  
  1193. /*
  1194. =================
  1195. ArenaServers_MenuKey
  1196. =================
  1197. */
  1198. static sfxHandle_t ArenaServers_MenuKey( int key ) {
  1199.     if( key == K_SPACE  && g_arenaservers.refreshservers ) {
  1200.         ArenaServers_StopRefresh();    
  1201.         return menu_move_sound;
  1202.     }
  1203.  
  1204.     if( ( key == K_DEL || key == K_KP_DEL ) && ( g_servertype == AS_FAVORITES ) &&
  1205.         ( Menu_ItemAtCursor( &g_arenaservers.menu) == &g_arenaservers.list ) ) {
  1206.         ArenaServers_Remove();
  1207.         ArenaServers_UpdateMenu();
  1208.         return menu_move_sound;
  1209.     }
  1210.  
  1211.     if( key == K_MOUSE2 || key == K_ESCAPE ) {
  1212.         ArenaServers_StopRefresh();
  1213.         ArenaServers_SaveChanges();
  1214.     }
  1215.  
  1216.  
  1217.     return Menu_DefaultKey( &g_arenaservers.menu, key );
  1218. }
  1219.  
  1220.  
  1221. /*
  1222. =================
  1223. ArenaServers_MenuInit
  1224. =================
  1225. */
  1226. static void ArenaServers_MenuInit( void ) {
  1227.     int            i;
  1228.     int            type;
  1229.     int            y;
  1230.     static char    statusbuffer[MAX_STATUSLENGTH];
  1231.  
  1232.     // zero set all our globals
  1233.     memset( &g_arenaservers, 0 ,sizeof(arenaservers_t) );
  1234.  
  1235.     ArenaServers_Cache();
  1236.  
  1237.     g_arenaservers.menu.fullscreen = qtrue;
  1238.     g_arenaservers.menu.wrapAround = qtrue;
  1239.     g_arenaservers.menu.draw       = ArenaServers_MenuDraw;
  1240.     g_arenaservers.menu.key        = ArenaServers_MenuKey;
  1241.  
  1242.     g_arenaservers.banner.generic.type  = MTYPE_BTEXT;
  1243.     g_arenaservers.banner.generic.flags = QMF_CENTER_JUSTIFY;
  1244.     g_arenaservers.banner.generic.x        = 320;
  1245.     g_arenaservers.banner.generic.y        = 16;
  1246.     g_arenaservers.banner.string          = "ARENA SERVERS";
  1247.     g_arenaservers.banner.style          = UI_CENTER;
  1248.     g_arenaservers.banner.color          = color_white;
  1249.  
  1250.     y = 80;
  1251.     g_arenaservers.master.generic.type            = MTYPE_SPINCONTROL;
  1252.     g_arenaservers.master.generic.name            = "Servers:";
  1253.     g_arenaservers.master.generic.flags            = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
  1254.     g_arenaservers.master.generic.callback        = ArenaServers_Event;
  1255.     g_arenaservers.master.generic.id            = ID_MASTER;
  1256.     g_arenaservers.master.generic.x                = 320;
  1257.     g_arenaservers.master.generic.y                = y;
  1258.     g_arenaservers.master.itemnames                = master_items;
  1259.  
  1260.     y += SMALLCHAR_HEIGHT;
  1261.     g_arenaservers.gametype.generic.type        = MTYPE_SPINCONTROL;
  1262.     g_arenaservers.gametype.generic.name        = "Game Type:";
  1263.     g_arenaservers.gametype.generic.flags        = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
  1264.     g_arenaservers.gametype.generic.callback    = ArenaServers_Event;
  1265.     g_arenaservers.gametype.generic.id            = ID_GAMETYPE;
  1266.     g_arenaservers.gametype.generic.x            = 320;
  1267.     g_arenaservers.gametype.generic.y            = y;
  1268.     g_arenaservers.gametype.itemnames            = servertype_items;
  1269.  
  1270.     y += SMALLCHAR_HEIGHT;
  1271.     g_arenaservers.sortkey.generic.type            = MTYPE_SPINCONTROL;
  1272.     g_arenaservers.sortkey.generic.name            = "Sort By:";
  1273.     g_arenaservers.sortkey.generic.flags        = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
  1274.     g_arenaservers.sortkey.generic.callback        = ArenaServers_Event;
  1275.     g_arenaservers.sortkey.generic.id            = ID_SORTKEY;
  1276.     g_arenaservers.sortkey.generic.x            = 320;
  1277.     g_arenaservers.sortkey.generic.y            = y;
  1278.     g_arenaservers.sortkey.itemnames            = sortkey_items;
  1279.  
  1280.     y += SMALLCHAR_HEIGHT;
  1281.     g_arenaservers.showfull.generic.type        = MTYPE_RADIOBUTTON;
  1282.     g_arenaservers.showfull.generic.name        = "Show Full:";
  1283.     g_arenaservers.showfull.generic.flags        = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
  1284.     g_arenaservers.showfull.generic.callback    = ArenaServers_Event;
  1285.     g_arenaservers.showfull.generic.id            = ID_SHOW_FULL;
  1286.     g_arenaservers.showfull.generic.x            = 320;
  1287.     g_arenaservers.showfull.generic.y            = y;
  1288.  
  1289.     y += SMALLCHAR_HEIGHT;
  1290.     g_arenaservers.showempty.generic.type        = MTYPE_RADIOBUTTON;
  1291.     g_arenaservers.showempty.generic.name        = "Show Empty:";
  1292.     g_arenaservers.showempty.generic.flags        = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
  1293.     g_arenaservers.showempty.generic.callback    = ArenaServers_Event;
  1294.     g_arenaservers.showempty.generic.id            = ID_SHOW_EMPTY;
  1295.     g_arenaservers.showempty.generic.x            = 320;
  1296.     g_arenaservers.showempty.generic.y            = y;
  1297.  
  1298.     y += 3 * SMALLCHAR_HEIGHT;
  1299.     g_arenaservers.list.generic.type            = MTYPE_SCROLLLIST;
  1300.     g_arenaservers.list.generic.flags            = QMF_HIGHLIGHT_IF_FOCUS;
  1301.     g_arenaservers.list.generic.id                = ID_LIST;
  1302.     g_arenaservers.list.generic.callback        = ArenaServers_Event;
  1303.     g_arenaservers.list.generic.x                = 72;
  1304.     g_arenaservers.list.generic.y                = y;
  1305.     g_arenaservers.list.width                    = MAX_LISTBOXWIDTH;
  1306.     g_arenaservers.list.height                    = 11;
  1307.     g_arenaservers.list.itemnames                = (const char **)g_arenaservers.items;
  1308.     for( i = 0; i < MAX_LISTBOXITEMS; i++ ) {
  1309.         g_arenaservers.items[i] = g_arenaservers.table[i].buff;
  1310.     }
  1311.  
  1312.     g_arenaservers.mappic.generic.type            = MTYPE_BITMAP;
  1313.     g_arenaservers.mappic.generic.flags            = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
  1314.     g_arenaservers.mappic.generic.x                = 72;
  1315.     g_arenaservers.mappic.generic.y                = 80;
  1316.     g_arenaservers.mappic.width                    = 128;
  1317.     g_arenaservers.mappic.height                = 96;
  1318.     g_arenaservers.mappic.errorpic                = ART_UNKNOWNMAP;
  1319.  
  1320.     g_arenaservers.arrows.generic.type            = MTYPE_BITMAP;
  1321.     g_arenaservers.arrows.generic.name            = ART_ARROWS0;
  1322.     g_arenaservers.arrows.generic.flags            = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
  1323.     g_arenaservers.arrows.generic.callback        = ArenaServers_Event;
  1324.     g_arenaservers.arrows.generic.x                = 512;
  1325.     g_arenaservers.arrows.generic.y                = 240-64+16;
  1326.     g_arenaservers.arrows.width                    = 64;
  1327.     g_arenaservers.arrows.height                = 128;
  1328.  
  1329.     g_arenaservers.up.generic.type                = MTYPE_BITMAP;
  1330.     g_arenaservers.up.generic.flags                = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
  1331.     g_arenaservers.up.generic.callback            = ArenaServers_Event;
  1332.     g_arenaservers.up.generic.id                = ID_SCROLL_UP;
  1333.     g_arenaservers.up.generic.x                    = 512;
  1334.     g_arenaservers.up.generic.y                    = 240-64+16;
  1335.     g_arenaservers.up.width                        = 64;
  1336.     g_arenaservers.up.height                    = 64;
  1337.     g_arenaservers.up.focuspic                    = ART_ARROWS_UP;
  1338.  
  1339.     g_arenaservers.down.generic.type            = MTYPE_BITMAP;
  1340.     g_arenaservers.down.generic.flags            = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
  1341.     g_arenaservers.down.generic.callback        = ArenaServers_Event;
  1342.     g_arenaservers.down.generic.id                = ID_SCROLL_DOWN;
  1343.     g_arenaservers.down.generic.x                = 512;
  1344.     g_arenaservers.down.generic.y                = 240+16;
  1345.     g_arenaservers.down.width                    = 64;
  1346.     g_arenaservers.down.height                    = 64;
  1347.     g_arenaservers.down.focuspic                = ART_ARROWS_DOWN;
  1348.  
  1349.     y = 376;
  1350.     g_arenaservers.status.generic.type        = MTYPE_TEXT;
  1351.     g_arenaservers.status.generic.x            = 320;
  1352.     g_arenaservers.status.generic.y            = y;
  1353.     g_arenaservers.status.string            = statusbuffer;
  1354.     g_arenaservers.status.style                = UI_CENTER|UI_SMALLFONT;
  1355.     g_arenaservers.status.color                = menu_text_color;
  1356.  
  1357.     y += SMALLCHAR_HEIGHT;
  1358.     g_arenaservers.statusbar.generic.type   = MTYPE_TEXT;
  1359.     g_arenaservers.statusbar.generic.x        = 320;
  1360.     g_arenaservers.statusbar.generic.y        = y;
  1361.     g_arenaservers.statusbar.string            = "";
  1362.     g_arenaservers.statusbar.style            = UI_CENTER|UI_SMALLFONT;
  1363.     g_arenaservers.statusbar.color            = text_color_normal;
  1364.  
  1365.     g_arenaservers.remove.generic.type        = MTYPE_BITMAP;
  1366.     g_arenaservers.remove.generic.name        = ART_REMOVE0;
  1367.     g_arenaservers.remove.generic.flags        = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
  1368.     g_arenaservers.remove.generic.callback    = ArenaServers_Event;
  1369.     g_arenaservers.remove.generic.id        = ID_REMOVE;
  1370.     g_arenaservers.remove.generic.x            = 440;
  1371.     g_arenaservers.remove.generic.y            = 88;
  1372.     g_arenaservers.remove.width                = 128;
  1373.     g_arenaservers.remove.height            = 64;
  1374.     g_arenaservers.remove.focuspic            = ART_REMOVE1;
  1375.  
  1376.     g_arenaservers.back.generic.type        = MTYPE_BITMAP;
  1377.     g_arenaservers.back.generic.name        = ART_BACK0;
  1378.     g_arenaservers.back.generic.flags        = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
  1379.     g_arenaservers.back.generic.callback    = ArenaServers_Event;
  1380.     g_arenaservers.back.generic.id            = ID_BACK;
  1381.     g_arenaservers.back.generic.x            = 0;
  1382.     g_arenaservers.back.generic.y            = 480-64;
  1383.     g_arenaservers.back.width                = 128;
  1384.     g_arenaservers.back.height                = 64;
  1385.     g_arenaservers.back.focuspic            = ART_BACK1;
  1386.  
  1387.     g_arenaservers.specify.generic.type        = MTYPE_BITMAP;
  1388.     g_arenaservers.specify.generic.name        = ART_SPECIFY0;
  1389.     g_arenaservers.specify.generic.flags    = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
  1390.     g_arenaservers.specify.generic.callback = ArenaServers_Event;
  1391.     g_arenaservers.specify.generic.id        = ID_SPECIFY;
  1392.     g_arenaservers.specify.generic.x        = 128;
  1393.     g_arenaservers.specify.generic.y        = 480-64;
  1394.     g_arenaservers.specify.width              = 128;
  1395.     g_arenaservers.specify.height              = 64;
  1396.     g_arenaservers.specify.focuspic         = ART_SPECIFY1;
  1397.  
  1398.     g_arenaservers.refresh.generic.type        = MTYPE_BITMAP;
  1399.     g_arenaservers.refresh.generic.name        = ART_REFRESH0;
  1400.     g_arenaservers.refresh.generic.flags    = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
  1401.     g_arenaservers.refresh.generic.callback    = ArenaServers_Event;
  1402.     g_arenaservers.refresh.generic.id        = ID_REFRESH;
  1403.     g_arenaservers.refresh.generic.x        = 256;
  1404.     g_arenaservers.refresh.generic.y        = 480-64;
  1405.     g_arenaservers.refresh.width            = 128;
  1406.     g_arenaservers.refresh.height            = 64;
  1407.     g_arenaservers.refresh.focuspic            = ART_REFRESH1;
  1408.  
  1409.     g_arenaservers.create.generic.type        = MTYPE_BITMAP;
  1410.     g_arenaservers.create.generic.name        = ART_CREATE0;
  1411.     g_arenaservers.create.generic.flags        = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
  1412.     g_arenaservers.create.generic.callback    = ArenaServers_Event;
  1413.     g_arenaservers.create.generic.id        = ID_CREATE;
  1414.     g_arenaservers.create.generic.x            = 384;
  1415.     g_arenaservers.create.generic.y            = 480-64;
  1416.     g_arenaservers.create.width                = 128;
  1417.     g_arenaservers.create.height            = 64;
  1418.     g_arenaservers.create.focuspic            = ART_CREATE1;
  1419.  
  1420.     g_arenaservers.go.generic.type            = MTYPE_BITMAP;
  1421.     g_arenaservers.go.generic.name            = ART_CONNECT0;
  1422.     g_arenaservers.go.generic.flags            = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
  1423.     g_arenaservers.go.generic.callback        = ArenaServers_Event;
  1424.     g_arenaservers.go.generic.id            = ID_CONNECT;
  1425.     g_arenaservers.go.generic.x                = 640;
  1426.     g_arenaservers.go.generic.y                = 480-64;
  1427.     g_arenaservers.go.width                    = 128;
  1428.     g_arenaservers.go.height                = 64;
  1429.     g_arenaservers.go.focuspic                = ART_CONNECT1;
  1430.  
  1431.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.banner );
  1432.  
  1433.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.master );
  1434.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.gametype );
  1435.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.sortkey );
  1436.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.showfull);
  1437.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.showempty );
  1438.  
  1439.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.mappic );
  1440.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.list );
  1441.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.status );
  1442.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.statusbar );
  1443.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.arrows );
  1444.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.up );
  1445.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.down );
  1446.  
  1447.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.remove );
  1448.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.back );
  1449.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.specify );
  1450.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.refresh );
  1451.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.create );
  1452.     Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.go );
  1453.  
  1454.     ArenaServers_LoadFavorites();
  1455.  
  1456.     g_servertype = Com_Clamp( 0, 3, ui_browserMaster.integer );
  1457.     g_arenaservers.master.curvalue = g_servertype;
  1458.  
  1459.     g_gametype = Com_Clamp( 0, 4, ui_browserGameType.integer );
  1460.     g_arenaservers.gametype.curvalue = g_gametype;
  1461.  
  1462.     g_sortkey = Com_Clamp( 0, 4, ui_browserSortKey.integer );
  1463.     g_arenaservers.sortkey.curvalue = g_sortkey;
  1464.  
  1465.     g_fullservers = Com_Clamp( 0, 1, ui_browserShowFull.integer );
  1466.     g_arenaservers.showfull.curvalue = g_fullservers;
  1467.  
  1468.     g_emptyservers = Com_Clamp( 0, 1, ui_browserShowEmpty.integer );
  1469.     g_arenaservers.showempty.curvalue = g_emptyservers;
  1470.  
  1471.     // force to initial state and refresh
  1472.     type = g_servertype;
  1473.     g_servertype = -1;
  1474.     ArenaServers_SetType( type );
  1475. }
  1476.  
  1477.  
  1478. /*
  1479. =================
  1480. ArenaServers_Cache
  1481. =================
  1482. */
  1483. void ArenaServers_Cache( void ) {
  1484.     trap_R_RegisterShaderNoMip( ART_BACK0 );
  1485.     trap_R_RegisterShaderNoMip( ART_BACK1 );
  1486.     trap_R_RegisterShaderNoMip( ART_CREATE0 );
  1487.     trap_R_RegisterShaderNoMip( ART_CREATE1 );
  1488.     trap_R_RegisterShaderNoMip( ART_SPECIFY0 );
  1489.     trap_R_RegisterShaderNoMip( ART_SPECIFY1 );
  1490.     trap_R_RegisterShaderNoMip( ART_REFRESH0 );
  1491.     trap_R_RegisterShaderNoMip( ART_REFRESH1 );
  1492.     trap_R_RegisterShaderNoMip( ART_CONNECT0 );
  1493.     trap_R_RegisterShaderNoMip( ART_CONNECT1 );
  1494.     trap_R_RegisterShaderNoMip( ART_ARROWS0  );
  1495.     trap_R_RegisterShaderNoMip( ART_ARROWS_UP );
  1496.     trap_R_RegisterShaderNoMip( ART_ARROWS_DOWN );
  1497.     trap_R_RegisterShaderNoMip( ART_UNKNOWNMAP );
  1498. }
  1499.  
  1500.  
  1501. /*
  1502. =================
  1503. UI_ArenaServersMenu
  1504. =================
  1505. */
  1506. void UI_ArenaServersMenu( void ) {
  1507.     ArenaServers_MenuInit();
  1508.     UI_PushMenu( &g_arenaservers.menu );
  1509. }                          
  1510.